import {
  BadRequestException,
  Injectable,
  UnauthorizedException
} from '@nestjs/common';
import { ConfigService } from '@nestjs/config';
import { JwtService } from '@nestjs/jwt';
import { AuthDto, User } from 'libs/db/src/schemas';
import { UserService } from '../system/user/user.service';
import { CryptoService } from 'src/modules/crypto/crypto.service';
import { RedisService } from '@dbs';

/**
 * 权限认证服务类
 */
@Injectable()
export class AuthService {
  userId: number;
  access_token: string = '';
  isLogin = false;
  constructor(
    private readonly userService: UserService,
    private readonly cryptoService: CryptoService,
    private readonly jwtService: JwtService,
    private readonly configService: ConfigService,
    private readonly redisService: RedisService
  ) {}

  /**
   * 登记，注册用户
   * @param user 用户信息
   */
  async login(dto: AuthDto) {
    // 1、从环境变量中获取配置
    const JWT_SECRET = this.configService.get<string>('JWT_SECRET');
    const JWT_EXPIRES_IN = this.configService.get<number>('JWT_EXPIRES_IN');

    // 2、结构，从user对象中解构出username和password
    const { username: name, password: pwd } = dto;
    // 3、检查，通过username判断数据库用户是否存在
    const isExist = await this.userService.existUser(name);
    if (!isExist) {
      throw new BadRequestException('用户不存在！');
    }

    // 4、查找， 根据username查找用户
    const userExists = await this.userService.findByUsername(name);
    // console.log(userExists);

    const { username, salt, password, _id } = userExists;

    // 5、验证，使用CryptoService加密password
    // 5.1、盐值处理，数据库的盐值只是加密盐值的一部分。安全考虑，把盐值分成两部分
    const handleSalt = this.cryptoService.getEntireSalt(salt);
    // 5.2、加密，使用CryptoService加密pwd
    const hash_pwd = this.cryptoService.encrypt(pwd, handleSalt);
    // console.log({ hash_pwd, password, handleSalt, salt });
    // 5.3、判断，如果用户存在，但密码不匹配，抛出BadRequestException异常
    if (password !== hash_pwd) {
      throw new BadRequestException('密码错误！');
    }
    this.isLogin = true;
    // 6、token处理
    // 6.1、预先处理token的payload有效载荷，包含username和userId
    const payload = { username, _id };

    // 6.2、生成token,使用jwtService.sign方法
    this.access_token = this.jwtService.sign(payload, {
      secret: JWT_SECRET
    });

    // 7、缓存token到redis
    await this.redisService.set(
      `token_${_id}`,
      this.access_token,
      JWT_EXPIRES_IN
    );

    // 返回处理后的token
    return {
      access_token: this.access_token,
      type: 'Bearer',
      _id,
      username
    };
  }

  /**
   * 刷新token
   * @param token token
   * @returns
   */
  async refresh() {
    const JWT_EXPIRES_IN = this.configService.get<number>('JWT_EXPIRES_IN');
    if (this.isLogin) {
      await this.redisService.set(
        `token_${this.userId}`,
        this.access_token,
        JWT_EXPIRES_IN
      );
    }
    return {
      access_token: this.access_token,
      type: 'Bearer',
      userId: this.userId
    };
  }

  /**
   * 注销用户
   * @param user 用户信息
   */
  async logout(id: string) {
    this.isLogin = false;
    await this.redisService.del(`token_${id}`);
  }
}
